package com.jame.context;

import com.jame.annotations.aop.AopChoiceAfter;
import com.jame.annotations.aop.AopChoiceAround;
import com.jame.annotations.aop.AopChoiceBefore;
import com.jame.annotations.aop.AopConfig;
import com.jame.annotations.bean.*;
import com.jame.aop.AopResult;
import com.jame.context.definition.ConfigurationBeanDefinition;
import com.jame.context.definition.UniversalBeanDefinition;
import com.jame.exception.CommentException;
import com.jame.exception.ParameterException;
import com.jame.other.StartImage;
import com.jame.typeenum.AnnotationType;
import com.jame.typeenum.ScopeType;
import com.jame.util.ClassUtil;
import org.apache.log4j.Logger;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


/**
 * @author : Jame
 * @date : 2021-06-13 15:40
 **/
public class ApplicationContext {

    private BeanContainer beanContainer = BeanContainer.getBeanContainer();

    private static final Logger logger = Logger.getLogger(ApplicationContext.class);

    public ApplicationContext(Class<?>... config) {
        /**
         * 启动字符画,没啥用
         */
        StartImage.printStartImage();

        ArrayList<Class<?>> classes = new ArrayList<>();
        List<ConfigurationBeanDefinition> beanDefinitions = new ArrayList<>();
        List<String> configScans = new ArrayList<>();

        boolean tag = false;
        for (Class<?> aClass : config) {
            ComponentScan componentScan = aClass.getAnnotation(ComponentScan.class);
            Configuration configuration = aClass.getAnnotation(Configuration.class);
            try {
                if (configuration == null) {
                    throw new CommentException(aClass.getSimpleName() + "没有添加@Configuration注解");
                }
            } catch (CommentException e) {
                e.printStackTrace();
            }
            if (componentScan != null) {
                List<String> collect = Arrays.stream(componentScan.value()).collect(Collectors.toList());
                configScans.addAll(collect);
                tag = true;
            }
        }
        if (!tag) {
            try {
                throw new CommentException("没有配置ComponentScan");
            } catch (CommentException e) {
                e.printStackTrace();
            }
        }

        /**
         * 获取扫描包下的配置类,可能只传入一个config配置类,里面只有一个scan
         * 但是同包或子包里可能还有其他的config配置类
         */
        List<Class> configurations = scanConfigurations(configScans, config);

        for (Class<?> aClass : configurations) {

            //注册aop配置类
            registerAop(aClass);


            if (aClass.getAnnotation(ComponentScan.class) == null) {
                continue;
            }
            ComponentScan annotation = aClass.getAnnotation(ComponentScan.class);
            String[] scans = annotation.value();
            for (String scan : scans) {
                classes.addAll(ClassUtil.getClasses(scan, false));
            }
            //获取配置类中所有方法,得到添加Bean的方法进行注册Bean
            Method[] methods = aClass.getDeclaredMethods();
            for (Method method : methods) {
                Bean bean = method.getAnnotation(Bean.class);
                if (bean == null) {
                    continue;
                }
                ConfigurationBeanDefinition configurationBeanDefinition = new ConfigurationBeanDefinition();
                try {
                    if (method.getParameterCount() != 0) {
                        throw new ParameterException("没有符合条件的构造");
                    }
                    Scope scope = method.getAnnotation(Scope.class);
                    if (scope != null && scope.scopeType() != ScopeType.Singleton) {
                        configurationBeanDefinition.setScopeType(ScopeType.Prototype);
                    }
                    configurationBeanDefinition.setMethod(method);
                    configurationBeanDefinition.setConfigObject(aClass.newInstance());
                } catch (ParameterException | IllegalAccessException | InstantiationException e) {
                    e.printStackTrace();
                }
                beanDefinitions.add(configurationBeanDefinition);

            }
            //初始化对象,存放到beanDefinition
            beanContainer.setConfigurationBeanDefinition(beanDefinitions);
            addUniversalBeanDefinition(classes);
            logger.info("<------------容器初始化完成---------->");

        }
    }

    /**
     * 获取传入扫描包下的配置类
     *
     * @return
     */
    private List<Class> scanConfigurations(List<String> scans, Class... configs) {
        //去重
        scans = scans.stream().distinct().collect(Collectors.toList());
        ArrayList<Class> configClasses = new ArrayList<>();
        for (String scan : scans) {
            configClasses.addAll(ClassUtil.getClasses(scan, true));
        }
        List<Class> configLists = Arrays.stream(configs).collect(Collectors.toList());
        configClasses.removeAll(configLists);
        configClasses.addAll(configLists);
        return configClasses;
    }

    /**
     * 添加通用beanDefinition
     *
     * @param classList
     */
    public void addUniversalBeanDefinition(List<Class<?>> classList) {
        List<Class<?>> classes = new ArrayList<>();
        List<UniversalBeanDefinition> beanDefinitions = new ArrayList<>();
        //去重
        classList = classList.stream().distinct().collect(Collectors.toList());

        for (Class<?> c : classList) {
            Component annotation = c.getAnnotation(Component.class);
            Configuration configuration = c.getAnnotation(Configuration.class);
            //判断是否添加了Component注解或configuration注解
            if (annotation == null && configuration == null) {
                continue;
            }
            //判断是否是抽象类
            //c.getModifiers返回这个类或接口的修饰符
            if (Modifier.isAbstract(c.getModifiers())) {
                continue;
            }
            classes.add(c);
        }

        for (Class<?> c : classes) {
            UniversalBeanDefinition bd = new UniversalBeanDefinition();
            Configuration annotation = c.getClass().getAnnotation(Configuration.class);
            if (annotation == null) {
                Scope scope = c.getAnnotation(Scope.class);
                if (scope != null && scope.scopeType() != ScopeType.Singleton) {
                    bd.setScopeType(ScopeType.Prototype);
                }
            } else {
                registerAop(c);
            }
            try {
                //懒加载
                if (c.getAnnotation(LazyLoad.class) == null) {
                    bd.setObject(c.newInstance());
                } else {
                    bd.setLazyLoad(true);
                }
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            bd.setName(c.getSimpleName());
            bd.setInterfaceClass(c);
            beanDefinitions.add(bd);
        }

        beanContainer.setUniversalBeanDefinition(beanDefinitions);

    }

    /**
     * 注册aop配置类
     *
     * @param c
     */
    private void registerAop(Class<?> c) {
        Object o = null;
        try {
            o = c.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        List<AopResult> aopResultList = new ArrayList<>();
        AopConfig config = c.getAnnotation(AopConfig.class);
        //说明是一个aop配置类
        if (config != null) {
            Method[] methods = c.getDeclaredMethods();
            for (Method method : methods) {
                AopResult aopResult = null;
                AnnotationType annotationType = null;
                String value = null;

                if (method.getAnnotation(AopChoiceBefore.class) != null) {
                    aopResult = new AopResult();
                    aopResult.setMethod(method);
                    annotationType = AnnotationType.Before;
                    value = method.getAnnotation(AopChoiceBefore.class).value();
                }
                if (method.getAnnotation(AopChoiceAfter.class) != null) {
                    aopResult = new AopResult();
                    aopResult.setMethod(method);
                    annotationType = AnnotationType.After;
                    value = method.getAnnotation(AopChoiceAfter.class).value();
                }
                if (method.getAnnotation(AopChoiceAround.class) != null) {
                    aopResult = new AopResult();
                    aopResult.setMethod(method);
                    annotationType = AnnotationType.Around;
                    value = method.getAnnotation(AopChoiceAround.class).value();
                }

                if (aopResult != null) {
                    aopResult.setValue(value);
                    aopResult.setAnnotationType(annotationType);
                    aopResult.setInstance(o);
                    aopResultList.add(aopResult);
                }
            }
        }
        if (aopResultList.size() != 0) {
            List<String> list = BeanAop.getBeanAop().getExecuteAop(aopResultList);
            beanContainer.setExecuteList(list);
        }
    }

    /**
     * 通过beanName获取
     *
     * @param <T>
     * @param beanName
     * @return
     */
    public <T> T getBean(String beanName) {
        return (T) beanContainer.doGetBean(beanName);
    }


    /**
     * 通过Class获取,其实也是通过beanName
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getBean(Class<T> clazz) {
        return (T) getBean(clazz.getSimpleName());
    }


}
